package cn.ycc1.functionlibrary.reflection;

/**
 * Invoking Constructors
 * @author ycc
 * @date 2025/3/9
 * A Constructor object lets you get more information on the corresponding constructor: its modifiers, the types and names of its
 * parameters, and enables you to invoke to create instance of objects, passing the arguments you need. This section also covers
 * how you can discover constructors in a class.
 */
public class InvokingConstructors {
    /**
     * Locating Constructors
     * There are two categories of methods provided in Class for accessing constructors.
     *
     * First, you can look for a specific constructor. These methods suppose you have the type of its parameter.
     * Second, you can either look for all the constructors that are declared in this class. In that case, you will get all the
     * constructors: public, protected, default (package) access, and private. Or you can look for the public constructors only.
     * Note that constructors are not inherited, so you do not have any equivalent to the methods you saw on fields or methods,
     * to locate elements on inherited classes.
     *
     * Class API	Array of constructors?	Private constructors?
     * getDeclaredConstructor(Class...)	no	yes
     * getConstructor(Class...)	no	no
     * getDeclaredConstructors()	yes	yes
     * getConstructors()	yes	no
     * A constructor declaration includes the name, modifiers, parameters, and list of throwable exceptions.
     * The java.lang.reflect.Constructor class provides a way to obtain this information.
     *
     * Let us see this method in action. The following code queries the constructors of the String class.
     *
     * The following code prints the 19 constructors you have in this class in the JDK 23. Note that not all of them are public.
     *
     * Class<?> c = String.class;
     * Constructor<?>[] constructors = c.getDeclaredConstructors();
     * System.out.println("# constructors = " + constructors.length);
     * for (Constructor<?> constructor : constructors) {
     *     System.out.println("constructor = " + constructor);
     * }
     * Running this code produces the following.
     *
     * # constructors = 19
     * constructor = public java.lang.String(java.lang.StringBuilder)
     * constructor = private java.lang.String(char[],int,int,java.lang.Void)
     * constructor = java.lang.String(java.lang.AbstractStringBuilder,java.lang.Void)
     * constructor = private java.lang.String(java.nio.charset.Charset,byte[],int,int)
     * constructor = public java.lang.String(byte[],int,int,java.nio.charset.Charset)
     * constructor = public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException
     * constructor = public java.lang.String(byte[],java.nio.charset.Charset)
     * constructor = public java.lang.String(byte[],int,int)
     * constructor = public java.lang.String(byte[])
     * constructor = public java.lang.String(java.lang.StringBuffer)
     * constructor = java.lang.String(byte[],byte)
     * constructor = public java.lang.String(char[],int,int)
     * constructor = public java.lang.String(char[])
     * constructor = public java.lang.String(java.lang.String)
     * constructor = public java.lang.String()
     * constructor = public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException
     * constructor = public java.lang.String(byte[],int)
     * constructor = public java.lang.String(byte[],int,int,int)
     * constructor = public java.lang.String(int[],int,int)
     * You can also locate a specific constructor, for instance, the one that takes an array of bytes. Note that the syntax
     * byte[].class is used to denote the class that corresponds to an array of bytes.
     *
     * Class<?> c = String.class;
     * Constructor<?> constructor = c.getConstructor(byte[].class);
     * System.out.println("constructor = " + constructor);
     * Running this code produces the following.
     *
     * constructor = public java.lang.String(byte[])
     */

    /**
     * Obtaining the Parameters of a Constructor
     * The Constructor class gives you access to the parameters of a given constructor. As for methods, you can get access to
     * the raw type with the method Constructor.getParameterTypes() method, or the generic type, with the
     * Constructor.getGenericParameterTypes() method.
     *
     * Let us see these two methods in action by querying the constructors of the ArrayList class.
     *
     * First, let us find the raw types of the parameters.
     *
     * Class<?> c = ArrayList.class;
     * Constructor<?>[] constructors = c.getConstructors();
     * for (Constructor<?> constructor : constructors) {
     *     System.out.println("constructor with " + constructor.getParameterCount() + " parameter");
     *     for (Class<?> type : constructor.getParameterTypes()) {
     *         System.out.println("   " + type.getName());
     *     }
     * }
     * Running this code produces the following.
     *
     * constructor with 1 parameter
     *    java.util.Collection
     * constructor with 0 parameter
     * constructor with 1 parameter
     *    int
     * You can get the generic types with the following code. Note that the Constructor.getGenericParameterTypes() method
     * returns an array of Type, on which you can call Type.getTypeName().
     *
     * Class<?> c = ArrayList.class;
     * Constructor<?>[] constructors = c.getConstructors();
     * for (Constructor<?> constructor : constructors) {
     *     System.out.println("constructor with " + constructor.getParameterCount() + " parameter");
     *     for (Type type : constructor.getGenericParameterTypes()) {
     *         System.out.println("   " + type.getTypeName());
     *     }
     * }
     * Running this code produces the following.
     *
     * constructor with 1 parameter
     *    java.util.Collection<? extends E>
     * constructor with 0 parameter
     * constructor with 1 parameter
     *    int
     */

    /**
     * Obtaining Exceptions of a Constructor
     * The Constructor class gives you access to the declared exceptions of a Constructor. These exceptions are returned in an
     * array, which can be of two types: an array of Class instances, or an array of Type instances. As usual the Type object
     * can give you information about a parameter type.
     *
     * Let us discover the exceptions thrown by the constructor of FileWriter that takes a String as a parameter.
     *
     * Class<?> c = FileWriter.class;
     * Constructor<?> constructor = c.getConstructor(String.class);
     * for (Class<?> exceptionType : constructor.getExceptionTypes()) {
     *     System.out.println("Exception type: " + exceptionType.getName());
     * }
     * for (Type exceptionType : constructor.getGenericExceptionTypes()) {
     *     System.out.println("Generic exception type: " + exceptionType.getTypeName());
     * }
     * Running this example produces the following:
     *
     * Exception type: java.io.IOException
     * Generic exception type: java.io.IOException
     */

    /**
     * Invocation Patterns
     * Invoking a constructor creates a new instance of a class. There are two reflective methods for creating instances of
     * classes: Class.newInstance() and Constructor.newInstance(), which is the pattern you should use.
     *
     * The first pattern consists in calling the newInstance() method of the Class. This method is actually calling the no-arg
     * constructor of the class it is invoked on. If this constructor does not exist or is not visible (for instance: it is
     * private), then it throws an exception.
     *
     * The Class.newInstance() method has been deprecated in Java SE 9, and should not be used anymore. There are several
     * reasons for that.
     *
     * Class.newInstance() can only invoke the zero-argument constructor, while Constructor.newInstance() may invoke any
     * constructor, regardless of the number of parameters.
     * Class.newInstance() throws any exception thrown by the constructor, regardless of whether it is checked or unchecked.
     * Constructor.newInstance() always wraps the thrown exception with an InvocationTargetException. So it bypasses the
     * compile-time exception checking, reason why it has been deprecated.
     * Class.newInstance() requires that the constructor be visible; Constructor.newInstance() may invoke private constructors
     * under certain circumstances.
     * The second pattern that you should be using now, consists in getting a reference on one of the constructor of the class,
     * and invoke it.
     *
     * Creating an Instance of String
     * The following example creates an instance of String.
     *
     * Class<String> stringClass = String.class;
     * String emptyString = stringClass.getConstructor().newInstance();
     * System.out.println("Empty? " + emptyString.isEmpty());
     * Running this code prints the following:
     *
     * Empty? true
     * Note the following:
     *
     * stringClass.getConstructor() throws a NoSuchMethodException, in the case the class does not have a no-arg constructor.
     * the call to newInstance() throws several exception:
     * InvocationTargetException: wrapping the exception the invocation of the constructor may have thrown.
     * InstantiationException: in case the corresponding class cannot be instantiated. This is the case for abstract classes or
     * interfaces.
     * IllegalAccessException: in case the no-arg constructor is not accessible from the calling code. This is the case if the
     * constructor is private, dans is accessed from another class.
     * Creating an Instance of a Record
     * Suppose that you have the following record.
     *
     * public record Point(int x, int y) {
     *     public Point() {
     *         this(0, 0);
     *     }
     * }
     * Then you can invoke its empty constructor with the following code.
     *
     * Class<Point> c = Point.class;
     * Constructor<Point> constructor = c.getConstructor();
     * Point p = constructor.newInstance();
     * System.out.println("p = " + p);
     * Running the previous code prints the following.
     *
     * p = Point[x=0, y=0]
     * You can also invoke its canonical constructor with the following code. Note that, despite it is not explicitly written
     * in the code, the compiler generates this canonical constructor for you. You can learn more on records in this section.
     *
     * Class<Point> c = Point.class;
     * Constructor<Point> constructor = c.getConstructor(int.class, int.class);
     * Point p = constructor.newInstance(1, 1);
     * System.out.println("p = " + p);
     * Running the previous code produces the following result.
     *
     * p = Point[x=1, y=1]
     */


}
